package lol.clann.minecraft.springboot.plugin.hook;

import com.ruoshui.utils.java.exception.ExceptionUtils;
import lol.clann.minecraft.springboot.api.context.PluginContext;
import lol.clann.minecraft.springboot.api.spi.sbp.ApplicationRunListener;
import lol.clann.minecraft.springboot.plugin.core.CustomLoggingSystem;
import lol.clann.minecraft.springboot.plugin.core.config.jackson.Jackson;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Consumer;

/**
 *
 * @author pyz
 * @date 2019/5/3 2:48 PM
 */
@Order(Integer.MIN_VALUE)
@Slf4j
public class RunListener implements SpringApplicationRunListener {
    private final SpringApplication application;
    private final String[] args;
    private List<ApplicationRunListener> listeners;

    public RunListener(SpringApplication application, String[] args) {
        //        替换spring默认的日志系统
        System.setProperty(LoggingSystem.SYSTEM_PROPERTY, CustomLoggingSystem.class.getName());
//        如果jackson配置还没好,出使用会出问题的
        Jackson.getObjectMapper();
        this.application = application;
        this.args = args;
        listeners = new ArrayList<>();

//        加载所有插件定义
        try {
            PluginContext.resolvePlugins();
        } catch (Exception e) {
            ExceptionUtils.throwException(e);
        }

        for (ApplicationRunListener applicationRunListener : ServiceLoader.load(ApplicationRunListener.class)) {
            listeners.add(applicationRunListener);
        }
        listeners.sort(Comparator.comparingInt(ApplicationRunListener::order));
    }

    @Override
    public void starting() {
        invoke(listener -> listener.starting(application));
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        invoke(listener -> listener.environmentPrepared(application, environment));
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        invoke(listener -> listener.contextPrepared(application, context));
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        invoke(listener -> listener.contextLoaded(application, context));
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        invoke(listener -> listener.started(application, context));
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        invoke(listener -> listener.running(application, context));
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        invoke(listener -> listener.failed(application, context, exception));
    }

    private void ensureLog(Runnable runnable) {
        LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
        Filter filter = loggerContext.getConfiguration().getFilter();
        if (filter != null) {
            loggerContext.getConfiguration().removeFilter(filter);
        }
        runnable.run();
        if (filter != null) {
            loggerContext.getConfiguration().addFilter(filter);
        }
    }

    private void invoke(Consumer<ApplicationRunListener> invoke) {
        for (ApplicationRunListener listener : listeners) {
            try {
                invoke.accept(listener);
            } catch (Throwable e) {
                LOGGER.error(listener.getClass().getName(), e);
            }
        }
    }
}
